/*
 * Created by SharpDevelop.
 * User: rseward
 * Date: 6/4/2008
 * Time: 6:02 AM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Text;
using System.Data;
using System.Collections.Generic;

using RD.Metadata.Database.Sql;
using log4net;

namespace AvvaMobileNHibernateHelperKit
{
	/// <summary>
	/// This BaseDatabaseDriver class should be extended by a driver classes for a specific database.
	/// </summary>
	public abstract class BaseDatabaseDriver
	{
		#region log4net
		protected static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
		#endregion
		
		public BaseDatabaseDriver()
		{
			LoadTypeSqlToNetMap();
			LoadDbTypeToNetMap();
			LoadNetTypeToFieldTypeMap();
		}
		
		protected Dictionary<string, string> typeSqlToNetMap = new Dictionary<string, string>();
		protected Dictionary<DbType, string> dbTypeToNetMap = new Dictionary<DbType, string>();
		protected Dictionary<string, string> netTypeToFieldTypeMap = new Dictionary<string, string>();
		
		
		public Dictionary<string, string> TypeSqlToNetMap {
			get { return typeSqlToNetMap; }
		}
		
		public abstract void LoadTypeSqlToNetMap();
		
		public abstract NHibernate.property GetHbmFieldProperty(SqlMetaColumn col, string propName, string colName);
		
		public void LoadDbTypeToNetMap()
		{
			Dictionary<DbType, string> map = dbTypeToNetMap;
			
			map[ DbType.AnsiString ] = "String";
			map[ DbType.AnsiStringFixedLength ] = "String";
			map[ DbType.Binary ] = "Boolean";
			map[ DbType.Boolean ] = "Boolean";
			map[ DbType.Byte ] = "Byte";
			map[ DbType.Currency ] = "Decimal";
			map[ DbType.Date ] = "DateTime";
			map[ DbType.DateTime ] = "DateTime";
			map[ DbType.DateTime2 ] = "DateTime";
			map[ DbType.Decimal ] = "Decimal";
			map[ DbType.Double ] = "Double";
			map[ DbType.Guid ] = "Guid";
			map[ DbType.Int16 ] = "Int16";
			map[ DbType.Int32 ] = "Int32";
			map[ DbType.Int64 ] = "Int64";
			map[ DbType.String ] = "String";
			map[ DbType.StringFixedLength ] = "String";
			// TODO: additonal DbType should be mapped.
		}
		
		public void LoadNetTypeToFieldTypeMap()
		{
			Dictionary<string, string> map = netTypeToFieldTypeMap;
			
			map[ "String" ] = "string";
			map[ "Boolean" ] = "bool";
			map[ "Byte" ] = "byte";
			map[ "Decimal" ] = "decimal";
			map[ "DateTime" ] = "DateTime";
			map[ "Double" ] = "double";
			map[ "Guid" ] = "Guid";
			map[ "Int16" ] = "int";
			map[ "Int32" ] = "int";
			map[ "Int64" ] = "Int64";
			map[ "int64" ] = "Int64";
		}
		
		public string GetNetType(DbType dbType)
		{
			if (dbTypeToNetMap.ContainsKey( dbType )) {
				return dbTypeToNetMap[ dbType ];
			}
			
			log.Warn("No mapping for " + dbType +" returning: "+dbType);
			
			return ""+dbType;
		}
		
		
		
		public string GetFieldType(DbType dbType)
		{
			string netType = GetNetType(dbType);
			string fieldType = netType;

			if ( netTypeToFieldTypeMap.ContainsKey( netType ) ) {
				fieldType = netTypeToFieldTypeMap[ netType ] ;
			}
			
			return fieldType;
		}
		
		public NHibernate.id GetHbmPrimaryKeyProperty(SqlMetaColumn col, string propName, string colName)
		{
			NHibernate.id idTag = new NHibernate.id();
			
			
			string netType = GetNetType( col.DbType );
			string unsavedValueAttr = "";
			string generatorStr = "identity";
			
			switch ( col.DbType ) {
				case DbType.Int16:
				case DbType.Int32:
				case DbType.Int64:
					unsavedValueAttr = "0";
					break;
				case DbType.Guid:
					generatorStr = "guid";
					unsavedValueAttr = ""+Guid.Empty;
					break;
			}
			
			idTag.name = propName;
			idTag.type = netType;
			if (!String.IsNullOrEmpty( unsavedValueAttr ) ) {
				idTag.unsavedvalue =unsavedValueAttr;
			}
			
			NHibernate.column xmlCol = new NHibernate.column();
			xmlCol.name = colName;
			xmlCol.sqltype = ""+col.DbType;
			xmlCol.notnull = !(col.IsNullable.Value);
			xmlCol.notnullSpecified = true;
			
			NHibernate.generator xmlGen = new NHibernate.generator();
			xmlGen.@class = generatorStr;
			
			idTag.column = new NHibernate.column[] { xmlCol };
			idTag.generator = xmlGen;
			
			return idTag;
		}
		
		public Dictionary<string, string> GetClassFieldDeclaration(Project project, string className, SqlMetaColumn col, string fieldName)
		{
			string fieldPrefix = "private";
			string fieldDeclaration ;
			string fieldType = "object";
			
			fieldType = GetFieldType( col.DbType );
			if (! "string".Equals(fieldType) ) {
				if (col.IsNullable.HasValue && col.IsNullable.Value) {
					fieldType = String.Format("{0}?", fieldType);
				}
			}
						
			fieldDeclaration = fieldPrefix + fieldType + " " + fieldName + "; /* DbType= "+ col.DbType+" */" ;
			
			
			Dictionary<string, string> fieldDecl = new Dictionary<string, string>();
			fieldDecl[ "fieldPrefix" ] = fieldPrefix;			
			fieldDecl[ "fieldType" ] = fieldType;
			fieldDecl[ "fieldName" ] = fieldName;
			fieldDecl[ "csharpDecl" ] = fieldDeclaration;
			
			return fieldDecl;
		}
		
		public Dictionary<string,string> GetClassPropertyDeclaration(Project project, string className, SqlMetaColumn col, string fieldName, string propName)
		{
			Dictionary<string,string> fieldDecl = GetClassFieldDeclaration(project, className, col, fieldName );
			
			string propPrefix = "public";
			fieldDecl[ "propPrefix" ] = propPrefix;
			fieldDecl[ "propName" ] = propName;
						
			StringBuilder propDeclaration = new StringBuilder( 200 );
			string fieldType = "object";
			
			fieldType = GetFieldType( col.DbType );
			if (! "string".Equals(fieldType) ) {			
				if (col.IsNullable.HasValue && col.IsNullable.Value) {
					fieldType = String.Format("{0}?", fieldType);
				}
			}
			string crlf = Constants.CRLF ;
			
			propDeclaration.Append( propPrefix + fieldType + " " + propName + " {"+ crlf);
			
			propDeclaration.Append("  get { return " + fieldName + "; }"+ crlf);
			propDeclaration.Append("  set { " + fieldName + " = value; }"+crlf);
			propDeclaration.Append("}"+crlf);
			
			fieldDecl[ "csharpPropDeclaration" ] = ""+propDeclaration;

			return fieldDecl;
		}
		
	}
}
